
#include "stdinc.h"
#include "AsmHook.h"
#include <Windows.h>
#include <vector>

#define HOOK_DEBUG 0


void HookDispatcher::init(unsigned long hookLoc, unsigned char takeoutSize,AsmHook *target)
{
#ifdef _DEBUG
	#if HOOK_DEBUG
		int3 = 0xCC;
	#else
		int3 = 0x90;
	#endif
#endif 
	addEsp = 0xC483;
	four = 4;
	pusha = 0x60;
	movecx = 0xB9;
	thisptr = (unsigned long) target;
	leaebx = 0x20245C8D;
	pushebx = 0x53;
	moveax = 0xB8;
	union
	{
		unsigned long address;
		void (__thiscall AsmHook::*func)(_stackReg*);
	} converter;
	converter.func = &AsmHook::hookfunc;
	address = (unsigned long) converter.address; 
	calleax = 0xD0FF; 
	popa = 0x61;
	memset(storedCode,0x90,MaxStoredCode);
	memcpy(storedCode,(void *)hookLoc,takeoutSize);
	pushadd = 0x68;
	retaddr = hookLoc+5;
	ret = 0xC3;
}

class HookDispatcherAllocator 
{
public:
	HookDispatcherAllocator()
		:mCurrentAlloc(0)
	{
		mDispatchers.push_back(allocateArray());
	}
	~HookDispatcherAllocator()
	{
		// this will be called at the very end of the applications lifecycle. There should be no hooks to be activated at this point
		// suffice to say, if there will be - hello segfault land
		for (std::vector<HookDispatcher*>::iterator i = mDispatchers.begin();i != mDispatchers.end(); i++)
			delete[] (*i);
	}
	HookDispatcher *getHookDispatcher()
	{
		HookDispatcher *result = (mDispatchers.rbegin())[mCurrentAlloc++];
		if (mCurrentAlloc == allocSize)
		{
			// get another array - we exceeded allocSize items;
			mDispatchers.push_back(allocateArray());
			mCurrentAlloc = 0;
		}
		return result;
	}
private:
	// a number taken from thin air - no real reason - should propably complement sizeof(HookDispatcher) to full page size
	static const int allocSize = 100; 

	HookDispatcher *allocateArray()
	{
		DWORD throwaway;
		HookDispatcher *array = new HookDispatcher[allocSize];
		VirtualProtectEx(GetCurrentProcess(),array,sizeof(HookDispatcher)*allocSize,PAGE_EXECUTE_READWRITE,&throwaway);
		return array;
	}
	std::vector<HookDispatcher*> mDispatchers;
	int mCurrentAlloc;
} gHookDispatcherList;



AsmHook::AsmHook(unsigned long address, unsigned char takeout_size)	
		:mAddress(address),mTakeoutSize(takeout_size)
	{
		DWORD throwaway;
		// allow writing to the code location being modified
		VirtualProtectEx(GetCurrentProcess(),(void*)mAddress,mTakeoutSize,PAGE_EXECUTE_READWRITE,&throwaway);
		// fetch a dispatcher from the executable memory block
		mDispatcher = gHookDispatcherList.getHookDispatcher();
		mDispatcher->init(mAddress,mTakeoutSize,this);
		// clobber the instructions at the target location
		memset((void*)mAddress,0x90,mTakeoutSize);
		
		// the call offset can(impropable, but still) be negative
		// the call offset is calculated from the NEXT instruction after the call, hence the +5
		long callOffset = (long) mDispatcher - (long)(address + 5);
		
		unsigned char *callLoc = (unsigned char *) mAddress;
		// call operand
		*callLoc = 0xE8;
		
		long * offsetLoc = (long *) (callLoc+1);
		// call offset
		*offsetLoc = callOffset;
	}